package com.aifast.system.sys.service;

import com.aifast.system.sys.entity.SysMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;

/**
 * @Author fujian
 * @since 2022/10/18 10:12
 *
 */
@Service
public class SseEmitterService {

    private static final Logger log = LoggerFactory.getLogger(SseEmitterService.class);
    private static Map<String, SseEmitter> map = new ConcurrentHashMap<>();

    /**
     * 创建连接
     * @param clientId 客户端id
     * @return
     */
    public SseEmitter connect(String clientId) {
        try {
            // 设置超时时间，0表示不过期。默认30秒
            SseEmitter sseEmitter = new SseEmitter(0L);
            // 注册回调
            setFunction(sseEmitter,clientId);
            map.put(clientId, sseEmitter);
            log.info("有新的客户端连接 ，clientId : {} ,当前在线人数 ：{}",clientId,map.size());
            return sseEmitter;
        } catch (Exception e) {
            log.error("创建新的sse连接异常，当前用户：{}", clientId);
        }
        return null;
    }

    /**
     * 注册回调方法
     * @param sseEmitter
     * @param clientId
     */
    private static void setFunction(SseEmitter sseEmitter,String clientId){
        sseEmitter.onCompletion(completionCallBack(clientId));
        sseEmitter.onError(errorCallBack(clientId));
        sseEmitter.onTimeout(timeoutCallBack(clientId));
    }

    private static void put(SseEmitter sseEmitter,String clientId){
        if(!map.containsKey(clientId)){
//            count.getAndIncrement();
        }
        map.put(clientId, sseEmitter);
    }

    /**
     * 移除当前客户端
     * @param clientId
     */
    private static void removeUser(String clientId) {
        map.remove(clientId);
//        count.getAndDecrement();
        log.info("移除用户：{}", clientId);
    }

    private static Consumer<Throwable> errorCallBack(String clientId) {
        return throwable -> {
            log.error("连接异常,客户端ID:{}", clientId);
            // 推送消息失败后，每隔10s推送一次，推送5次
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(10000);
                    SseEmitter sseEmitter = map.get(clientId);
                    if (sseEmitter == null) {
                        log.error("第{}次消息重推失败,未获取到 {} 对应的长链接", i + 1, clientId);
                        continue;
                    }
                    sseEmitter.send("失败后重新推送");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
    }

    private static Runnable timeoutCallBack(String clientId) {
        return () -> {
            log.info("连接超时：{}", clientId);
            removeUser(clientId);
        };
    }

    private static Runnable completionCallBack(String clientId) {
        return () -> {
            log.info("结束连接：{}", clientId);
            removeUser(clientId);
        };
    }

    /**
     * 发送消息
     * @param clientId 发送对象
     * @param msg 消息内容
     * @return
     */
    public boolean send(String clientId , String msg) {
        if(map.containsKey(clientId)){
            try {
                map.get(clientId).send(msg);
                return true;
            } catch (IOException e) {
                log.error("用户[{}]推送异常:{}", clientId, e.getMessage());
                removeUser(clientId);
                return false;
            }
        }
        return false;
    }

    /**
     *
     * @param clientId
     * @param message
     * @return
     */
    public boolean send(String clientId, SysMessage message){

        if(map.containsKey(clientId)){
            try {
                map.get(clientId).send(message);
                return true;
            } catch (IOException e) {
                log.error("用户[{}]推送异常:{}", clientId, e.getMessage());
                removeUser(clientId);
                return false;
            }
        }

        return false;
    }

    /**
     * 关闭客户端
     * @param clientId 客户端id
     */
    public void close(String clientId) {
        removeUser(clientId);
    }
}
